#ifndef SIMPLE_SUPPORT_TUPLE_UTILS_REDUCTION_HPP
#define SIMPLE_SUPPORT_TUPLE_UTILS_REDUCTION_HPP

#include <tuple> // std::apply
#include <type_traits> // std::is_same_v std::is_constructible_v std::enable_if_t std::remove_cvref_t
#include <utility> // std::forward

#include "../type_traits/is_template_instance.hpp" // is_template_instance_v

namespace simple::support
{

	// TODO: this is way too recursive...
	template <typename Value, typename BOp>
	struct tuple_deep_reduce
	{
		template <typename First, typename... Rest>
		constexpr Value operator()(First&& first, Rest&&... rest) const
		{
			auto& self = *this;
			if constexpr (std::is_same_v<std::remove_cvref_t<First>, Value>)
			{
				if constexpr (sizeof...(rest) == 0)
				{
					return std::forward<First>(first);
				}
				else
				{
					return BOp{}(std::forward<First>(first), self(std::forward<Rest>(rest)...));
				}
			}
			else
			{
				if constexpr (sizeof...(rest) == 0)
				{
					return std::apply(self, std::forward<First>(first));
				}
				else
				{
					return BOp{}(std::apply(self, std::forward<First>(first)), self(std::forward<Rest>(rest)...));
				}
			}
		}
	};

	template <typename Value, typename BOp, typename T>
	struct tuple_reduction
	{
		T value;

		template <typename TT>
		constexpr tuple_reduction(TT value) : value(value) { }

		constexpr operator T() const { return value; }

		template <typename Other, typename TT = T,
			std::enable_if_t<
				std::is_constructible_v<Other,T> &&
				not support::is_template_instance_v<
					simple::support::tuple_reduction, Other
				>
			> * = nullptr
		>
		constexpr operator Other() const { return static_cast<Other>(value); }

		explicit constexpr operator Value()
		{
			return tuple_deep_reduce<Value,BOp>{}(value);
		}
	};

} // namespace simple::support

#endif /* end of include guard */
